# ==============================================
# 导入层
# ==============================================
from . import config as config_module           # 导入配置模块中的全局变量
from .lanying_HID import BlueHid                # 导入已加载的蓝影键鼠模块
import time                                     # 时间操作模块（用于延时、计时等）
from ascript.android.node import Selector       # 导AS控件包（用于安卓界面控件选择操作）
from datetime import datetime                   # 日期时间模块（用于生成时间戳）
import random                                   # 随机数模块（用于生成随机文件名）
from ascript.android import screen              # 导入屏幕操作模块  
from .feishu_api import *                       # 导入飞书API模块
from airscript.system import R as airscript_R   # 导入资源路径工具
from ascript.android.system import Device       # 获取系统信息
from PIL import Image                           # 新增图像处理库导入
import pandas as pd                             # 添加pandas库导入
import re                                       # 正则表达式
import os                                       # 操作系统接口模块（用于文件删除等操作）
from ascript.android.screen import Ocr          # 导入OCR模块
from ascript.android.system import Clipboard    # 导入剪贴板模块


# ==============================================
# 封装层
# ==============================================

# 获取屏幕信息
display = Device.display()
# 屏幕宽度
wide = display.widthPixels
# 屏幕高度
High = display.heightPixels

# 封装蓝牙HID键鼠控件实现稳当滑动
def scroll_screen(direction='up', scroll_pixels=300, press_delay=100, scroll_duration=3000, lift_delay=50):
    """控制蓝牙键鼠执行像素级滚动操作
    参数：
        direction: str 滚动方向，可选 'up'（上滑）/'down'（下滑）
        scroll_pixels: 滚动像素数（默认300像素）
        press_delay: int 按下延迟（单位：毫秒，默认0 建议写0,如果想拖动,可以写50~100）
        scroll_duration: int 滚动持续时间（单位：毫秒，根据像素动态计算)
        lift_delay: int 抬起延迟（单位：毫秒，默认0 建议写0写10~50 不写默认0）
    返回：
        None 通过蓝牙HID直接控制设备操作
    """
    # 参数有效性检查
    if direction not in ['up', 'down']:
        print(f"警告：{direction} 不是有效方向，默认使用'up'")
        direction = 'up'

    # 动态计算滚动时长（每像素5ms的滑动速度）
    # scroll_duration = int(scroll_pixels * 5)

    start_x = int(wide * 0.5)
    start_y = int(High * 0.5)

    # 根据方向计算终止坐标
    if direction == 'up':
        end_y = start_y - scroll_pixels
    else:
        end_y = start_y + scroll_pixels  # 向下滑动时增加Y坐标值

    # 执行蓝牙HID滚动操作（使用蓝影智连模块）
    BlueHid.swipEx(
        start_x, start_y,  # 起始坐标（屏幕正中心）
        start_x, end_y,  # 终止坐标（Y轴偏移）
        press_delay,  # 参数化按下延迟
        scroll_duration,  # 动态计算的滚动持续时间
        lift_delay  # 参数化抬起延迟
    )

# 截取全屏、指定范围截图并保存到本地
def capture_local(region=None):
    """截取全屏、指定范围截图并保存到本地
    参数：
        region: 示例坐标：左100，顶200，右900，底1600 ，不填写则为全屏

    返回：
        full_path： 文件路径
    """
    try:
        # 获取PIL图像对象
        pil_img = screen.capture(
            x=region[0] if region else None,
            y=region[1] if region else None,
            x1=region[2] if region else None,
            y1=region[3] if region else None,
            pixel_format=screen.FORMAT_PIL_IMAGE
        )

        # 生成带时间戳和随机数编号的唯一文件名
        image_path = f"longshot_{datetime.now().strftime('%Y%m%d%H%M%S')}_{random.randint(1000, 9999)}.png"
        full_path = airscript_R.sd(image_path)

        # 直接保存PIL图像到SD卡
        pil_img.save(full_path)
        pil_img.close()  # 释放安卓系统资源

        return full_path
    except Exception as e:
        print(f"[错误] 截图失败：{str(e)}")
        return None

# 查询聊天页面的有效对话内容，返回包含图像信息的DataFrame
def find_LinearLayout():
    """查询聊天页面的有效对话内容（未被遮挡），并返回包含PIL图像对象和位置信息的DataFrame"""

    # 第一步：查询聊天内容的容器控件（RecyclerView）
    # 使用控件选择器，指定类型为RecyclerView，深度为9，且可点击
    nodes = Selector().type("RecyclerView").depth(9).clickable(True).find_all()
    
    # 确保nodes不是None
    if nodes is None:
        nodes = []

    # 判断是否找到了RecyclerView控件
    if nodes:
        # 获取第一个RecyclerView控件的位置信息
        Rect = nodes[0].rect  # 获取控件范围值，返回Rect对象

        # 提取RecyclerView控件的边界坐标，用于后续判断消息是否完全可见
        left = Rect.left  # 左上坐标x（控件左边界）
        top = Rect.top  # 左上坐标y（控件上边界）
        right = Rect.right  # 右下坐标x（控件右边界）
        bottom = Rect.bottom  # 右下坐标y（控件下边界）

        # 第二步：在RecyclerView内查询所有聊天消息项（LinearLayout控件）
        # 使用控件选择器，指定类型为LinearLayout，可点击，可见，深度为10
        LinearLayout_nodes = Selector().type("LinearLayout").clickable(True).depth(10).find_all()
        
        # 确保LinearLayout_nodes不是None
        if LinearLayout_nodes is None:
            LinearLayout_nodes = []

        # 判断是否找到了LinearLayout控件（聊天消息项）
        if LinearLayout_nodes:
            print(f"[查询] 检测到对话数量: {len(LinearLayout_nodes)}")
            # 将列表倒序，使消息按从上到下的顺序排列（通常最新消息在底部）
            reversed_LinearLayout = list(reversed(LinearLayout_nodes))

            # 创建存储各类数据的字典容器
            data = {
                'iitem': [],  # 存储原始控件对象
                'top': [],  # 存储消息顶部y坐标
                'bottom': [],  # 存储消息底部y坐标
                'height': [],  # 存储消息高度
                'pil_image': [],  # 存储消息截图（PIL图像对象）
                'image_hash': []  # 存储图像哈希值（用于后续去重）
            }

            # 第三步：遍历所有消息控件，提取信息
            for iitem in reversed_LinearLayout:
                # 获取当前消息控件的位置信息
                LinearLayout_Rect = iitem.rect  # 获取控件范围值

                # 提取消息控件的四个边界坐标
                LinearLayout_left = LinearLayout_Rect.left  # 左上坐标x1
                LinearLayout_top = LinearLayout_Rect.top  # 左上坐标y1
                LinearLayout_right = LinearLayout_Rect.right  # 右下坐标x2
                LinearLayout_bottom = LinearLayout_Rect.bottom  # 右下坐标y2

                # 关键处理：判断消息是否完全可见（不被父容器遮挡）
                if LinearLayout_top < top or LinearLayout_bottom > bottom:
                    # 消息被部分遮挡（顶部超出视图或底部超出视图）
                    # 忽略此消息，确保只处理完全可见的内容
                    continue

                    # 对当前消息控件进行截图，获取PIL图像对象
                first_img = screen.capture(
                    LinearLayout_left,
                    LinearLayout_top,
                    LinearLayout_right,
                    LinearLayout_bottom,
                    pixel_format=screen.FORMAT_PIL_IMAGE
                )

                # 为图像生成哈希值，作为图像内容的"指纹"，用于后续识别相同/相似内容
                img_hash = image_hash(first_img)

                # 计算消息控件的高度（像素）
                height = LinearLayout_bottom - LinearLayout_top

                # 将当前消息的各项数据添加到对应的字典列表中
                data['iitem'].append(iitem)  # 存储原始控件对象（便于后续操作）
                data['top'].append(LinearLayout_top)  # 存储顶部y坐标（用于确定位置）
                data['bottom'].append(LinearLayout_bottom)  # 存储底部y坐标（用于确定位置）
                data['height'].append(height)  # 存储高度（用于布局计算）
                data['pil_image'].append(first_img)  # 存储截图（用于最终拼接）
                data['image_hash'].append(img_hash)  # 存储哈希值（用于内容匹配）

            # 第四步：创建DataFrame并增强数据关联性
            # 将收集的数据转换为DataFrame格式，方便后续处理
            df = pd.DataFrame(data)

            # 为每条消息添加前后消息引用，增强上下文关联
            # 初始化存储前后消息哈希值的列
            df['prev_hashes'] = None  # 存储前两条消息的哈希值
            df['next_hashes'] = None  # 存储后两条消息的哈希值

            # 为每条消息建立与前后消息的关联关系
            for i in range(len(df)):
                # 处理前向关联：获取前1-2条消息的哈希值
                prev_dict = {}  # 初始化前向消息字典
                for j in range(1, 3):  # 循环处理"前1"和"前2"
                    if i - j >= 0:  # 确保索引在有效范围内
                        prev_dict[f'前{j}'] = df.loc[i - j, 'image_hash']  # 记录前j条消息的哈希值
                    else:
                        prev_dict[f'前{j}'] = None  # 如果索引无效，设为None

                # 处理后向关联：获取后1-2条消息的哈希值
                next_dict = {}  # 初始化后向消息字典
                for j in range(1, 3):  # 循环处理"后1"和"后2"
                    if i + j < len(df):  # 确保索引在有效范围内
                        next_dict[f'后{j}'] = df.loc[i + j, 'image_hash']  # 记录后j条消息的哈希值
                    else:
                        next_dict[f'后{j}'] = None  # 如果索引无效，设为None

                # 将前后消息引用字典存储到DataFrame中
                df.at[i, 'prev_hashes'] = prev_dict  # 存储前向消息引用
                df.at[i, 'next_hashes'] = next_dict  # 存储后向消息引用

            # 输出有效处理的消息数量
            print(f"[查询] 有效对话数量: {len(df)}")

            # 返回处理完成的DataFrame
            return df

        else:
            # 未找到聊天消息控件，返回空DataFrame
            print('[查询] 没有找到聊天消息控件')
            return pd.DataFrame()  # 返回空DataFrame

    else:
        # 未找到聊天容器控件，返回空DataFrame
        print('[查询] 没有找到聊天容器控件')
        return pd.DataFrame()  # 返回空DataFrame

# 图片拼接
def merge_images(images):
    """
    将多张PIL图像垂直拼接为一张长图并保存

    参数:
        images: PIL Image对象列表，需确保所有图像宽度一致

    返回:
        str: 拼接后图像的保存路径
    """
    # 校验输入是否为空
    if not images:
        raise ValueError("输入的图像列表不能为空")

    # 获取第一张图像的宽度作为拼接后图像的宽度
    total_width = images[0].width

    # 计算所有图像高度之和作为拼接后图像的总高度
    total_height = sum(img.height for img in images)

    # 创建一个新的RGB图像，尺寸为总宽度和总高度之和
    composite = Image.new('RGB', (total_width, total_height))

    # 当前粘贴位置的Y坐标偏移量，初始为0
    y_offset = 0

    # 遍历所有图像，将它们依次粘贴到新图像中
    for img in images:
        # 将当前图像粘贴到合成图像的指定位置
        composite.paste(img, (0, y_offset))

        # 更新Y坐标偏移量，为下一张图像的粘贴做准备
        y_offset += img.height

    # 生成带时间戳的保存路径，确保文件名唯一性
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
    save_path = airscript_R.sd(f"chat_screenshot_{timestamp}.png")

    # 保存拼接后的图像
    composite.save(save_path)

    # 释放所有原始图像资源，避免内存泄漏
    for img in images:
        img.close()

    # 释放合成图像资源
    composite.close()

    # 返回保存路径供后续使用
    return save_path

# 生成图像哈希值
def image_hash(img, hash_size=16):
    """
    生成简化的图像哈希，更适合处理有动态表情的截图
    使用更小的hash_size以降低敏感度，忽略细微变化
    """
    # 缩放到更小的尺寸，降低敏感度
    fixed_size = (hash_size, hash_size)
    img = img.resize(fixed_size, Image.Resampling.LANCZOS)

    # 转为灰度图
    gray = img.convert('L')

    # 计算整个图像的像素均值
    pixels = list(gray.getdata())
    avg = sum(pixels) / len(pixels)

    # 生成二值化哈希
    return ''.join(['1' if pixel > avg else '0' for pixel in pixels])

# 哈希距离计算函数
def hamming_distance(hash1, hash2):
    """计算两个哈希值之间的汉明距离"""
    # 确保两个哈希值长度相同
    if len(hash1) != len(hash2):
        raise ValueError("哈希值长度不匹配")

    # 计算不同位的数量
    return sum(c1 != c2 for c1, c2 in zip(hash1, hash2))

# 智能合并两个DataFrame
def merge_dataframes(df1, df2, hash_threshold=5):
    """
    智能合并两个DataFrame，通过分析消息的上下文关系找到最佳拼接点

    参数:
        df1, df2: 两个要合并的DataFrame
        hash_threshold: 图像哈希值汉明距离阈值，小于此值视为相同图像
    """
    # 处理空DataFrame的边界情况
    if df1.empty:
        return df2  # 如果第一个DataFrame为空，直接返回第二个
    if df2.empty:
        return df1  # 如果第二个DataFrame为空，直接返回第一个

    # 初始化最佳匹配点的参数
    best_match = None  # 存储最佳匹配结果
    best_match_score = 0  # 最佳匹配的评分，越高表示匹配度越好
    best_df1_idx = -1  # df1中的最佳匹配索引位置
    best_df2_idx = -1  # df2中的最佳匹配索引位置

    # 优化搜索范围：只在df1后半部分和df2前半部分中寻找匹配点
    # 这是基于滚动截图的特性，重叠部分通常在df1尾部和df2头部
    df1_search_range = df1.iloc[len(df1) // 2:]  # df1的后半部分
    df2_search_range = df2.iloc[:len(df2) // 2]  # df2的前半部分

    # 第一阶段：查找最佳匹配点，通过嵌套循环比较所有可能的配对
    for i, row1 in df1_search_range.iterrows():
        hash1 = row1['image_hash']  # 获取df1当前行的图像哈希值
        next_hashes1 = row1['next_hashes']  # 获取df1当前行后续消息的哈希值字典

        for j, row2 in df2_search_range.iterrows():
            hash2 = row2['image_hash']  # 获取df2当前行的图像哈希值
            prev_hashes2 = row2['prev_hashes']  # 获取df2当前行前置消息的哈希值字典

            # 计算两个消息图像哈希的汉明距离（差异程度）
            distance = hamming_distance(hash1, hash2)

            # 如果距离小于阈值，认为是潜在匹配点
            if distance < hash_threshold:
                # 计算基础匹配分数：距离越小，分数越高
                match_score = 10 - distance

                # 初始化上下文匹配计数器，用于增强匹配置信度
                context_matches = 0

                # 检查上下文一致性（1）：df1中当前消息的后续消息是否与df2中对应位置匹配
                if next_hashes1 and j + 1 < len(df2):  # 确保有后续消息且df2有足够的元素
                    for offset, next_hash in next_hashes1.items():  # 遍历后续消息哈希
                        # 提取偏移量数字（从"后1"、"后2"等字符串中）
                        if next_hash and j + int(offset[-1]) < len(df2):  # 确保索引有效
                            next_idx = j + int(offset[-1])  # 计算df2中对应的索引
                            df2_hash = df2.iloc[next_idx]['image_hash']  # 获取对应消息哈希
                            # 比较哈希相似度
                            if hamming_distance(next_hash, df2_hash) < hash_threshold:
                                context_matches += 1  # 上下文匹配数+1

                # 检查上下文一致性（2）：df2中当前消息的前置消息是否与df1中对应位置匹配
                if prev_hashes2 and i - 1 >= 0:  # 确保有前置消息且df1有足够的元素
                    for offset, prev_hash in prev_hashes2.items():  # 遍历前置消息哈希
                        # 提取偏移量数字（从"前1"、"前2"等字符串中）
                        if prev_hash and i - int(offset[-1]) >= 0:  # 确保索引有效
                            prev_idx = i - int(offset[-1])  # 计算df1中对应的索引
                            df1_hash = df1.iloc[prev_idx]['image_hash']  # 获取对应消息哈希
                            # 比较哈希相似度
                            if hamming_distance(prev_hash, df1_hash) < hash_threshold:
                                context_matches += 1  # 上下文匹配数+1

                # 增加上下文匹配的权重：每个上下文匹配增加5分
                # 这使得具有更多上下文匹配的配对得分更高
                match_score += context_matches * 5

                # 更新最佳匹配：如果当前匹配分数更高
                if match_score > best_match_score:
                    best_match_score = match_score  # 更新最高分数
                    best_df1_idx = i  # 更新df1最佳匹配索引
                    best_df2_idx = j  # 更新df2最佳匹配索引

    # 第二阶段：基于找到的最佳匹配点合并DataFrame
    if best_df1_idx >= 0 and best_df2_idx >= 0:  # 如果找到有效的匹配点
        print(f"[合并] 找到最佳匹配点，匹配分数={best_match_score}")

        # 合并两个DataFrame，移除重复部分：
        # 1. 保留df1从开始到匹配点（包含）的部分
        # 2. 连接df2从匹配点之后的部分
        result = pd.concat([
            df1.iloc[:best_df1_idx + 1],  # df1直到匹配点(包含)
            df2.iloc[best_df2_idx + 1:]  # df2从匹配点之后开始
        ], ignore_index=True)  # 重置索引以保持连续性

        return result  # 返回智能合并结果

    # 第三阶段：备选方案 - 优化的重复检测拼接
    print("[合并] 执行优化拼接")

    # 创建一个集合来跟踪已存在的消息哈希，用于重复检测
    existing_hashes = set(df1['image_hash'].tolist())

    # 将df2中的非重复消息添加到结果中
    unique_df2_rows = []
    duplicates_found = 0

    for _, row in df2.iterrows():
        current_hash = row['image_hash']

        # 检查当前消息是否已存在
        if current_hash in existing_hashes:
            duplicates_found += 1
            continue  # 跳过重复消息
        else:
            # 添加到唯一行列表，并记录此哈希值
            unique_df2_rows.append(row)
            existing_hashes.add(current_hash)

    # 构建只包含唯一消息的df2
    unique_df2 = pd.DataFrame(unique_df2_rows) if unique_df2_rows else pd.DataFrame(columns=df2.columns)

    # 最终合并
    if not unique_df2.empty:
        result = pd.concat([df1, unique_df2], ignore_index=True)
        print(f"[合并] 删除了 {duplicates_found} 条重复消息")
    else:
        result = df1.copy()  # 如果没有新的唯一消息，直接返回df1
        print("[合并] 无新增内容")

    # 返回最终合并结果
    return result

# 滚动查询聊天记录
def capture_chat_long_screenshot(
        scroll_times=15,  # 滚屏次数：最多滚动屏幕的次数，默认15次
        scroll_pixels=800,  # 每次滚动的像素距离：控制每次上滑的距离，默认800像素
        scroll_delay=1.5,  # 每次滚动后等待的秒数：等待界面稳定的时间，默认1.5秒
        hash_threshold=5,  # 哈希匹配阈值：判断两张图片相似度的标准，值越小要求越严格
        press_delay=100,  # 按下延迟(毫秒)：模拟手指按下屏幕的延迟时间
        scroll_duration=3000,  # 滚动持续时间(毫秒)：控制滑动速度，值越大滑动越慢
        lift_delay=50  # 抬起延迟(毫秒)：模拟手指离开屏幕的延迟时间
):
    """
    捕获多屏聊天内容并生成长截图

    参数:
        scroll_times: 滚屏次数
        scroll_pixels: 每次滚动的像素距离
        scroll_delay: 每次滚动后等待的秒数
        hash_threshold: 哈希匹配阈值
        press_delay: 按下延迟(毫秒)
        scroll_duration: 滚动持续时间(毫秒)
        lift_delay: 抬起延迟(毫秒)

    返回:
        result_path: 生成的长截图保存路径，失败时返回None
    """

    # 打印函数配置信息，便于调试和记录
    print(f"[截图] 开始采集聊天记录，预计滚动 {scroll_times} 次")
    print(f"[截图] 获取第一屏聊天内容")
    df_current = find_LinearLayout()  # 调用find_LinearLayout函数获取当前屏幕上的聊天内容

    # 检查是否成功获取到对话内容
    if df_current.empty:
        print("[错误] 未找到有效对话内容，操作终止")  # 如果没有找到对话内容，终止操作
        return None  # 返回None表示操作失败

    print(f"[截图] 第一屏获取到 {len(df_current)} 条对话")  # 打印找到的对话数量

    # 初始化合并结果DataFrame为第一屏数据
    merged_df = df_current

    # 第二步：循环滚动屏幕，获取更多聊天内容并合并
    for scroll_count in range(scroll_times):
        # 滑动屏幕获取更多内容
        print(f"[截图] 滑动获取第 {scroll_count + 2} 屏内容")

        # 调用scroll_screen函数上滑屏幕以显示更早的聊天记录
        scroll_screen(
            direction='up',  # 向上滑动，查看历史消息
            scroll_pixels=scroll_pixels,  # 设置滑动距离
            press_delay=press_delay,  # 设置按下延迟
            scroll_duration=scroll_duration,  # 设置滑动持续时间
            lift_delay=lift_delay  # 设置抬起延迟
        )

        # 等待界面滑动完成并重新渲染
        time.sleep(scroll_delay)

        # 获取当前屏幕的聊天内容
        print(f"[截图] 获取第{scroll_count + 2}屏聊天内容...")
        df_next = find_LinearLayout()  # 再次调用find_LinearLayout获取新的一屏内容

        # 检查是否获取到新的对话内容
        if df_next.empty:
            print(f"[截图] 第 {scroll_count + 2} 屏未找到有效对话内容，停止滚动")
            break  # 如果没有找到内容，中断循环

        print(f"[截图] 第 {scroll_count + 2} 屏获取到 {len(df_next)} 条对话")

        # 合并当前数据与新获取的数据
        print(f"[合并] 合并第{scroll_count + 1}屏和第{scroll_count + 2}屏数据...")

        # 记录合并前的对话数量
        old_count = len(merged_df)

        # 调用merge_dataframes函数智能合并两屏数据，避免重复内容
        # hash_threshold参数控制图像匹配的严格程度
        merged_df = merge_dataframes(merged_df, df_next, hash_threshold=hash_threshold)

        # 获取合并后的对话数量
        new_count = len(merged_df)

        # 打印合并结果统计信息
        print(f"[合并] 合并结果: 原有 {old_count} 条 + 新增 {len(df_next)} 条 = 合并后 {new_count} 条")
        print(f"[消息] 新增有效内容: {new_count - old_count} 条")

        # 重要优化：检测是否已到达聊天记录顶部
        # 如果合并后的总消息数没有增加，说明没有新内容被添加，可能已到达聊天记录顶部
        if new_count <= old_count:
            print("[截图] 未获取到新内容，可能已到达对话顶部")
            print("自动停止滚动以避免无效操作")
            break  # 立即终止循环，不再继续滚动

    # 第三步：所有滚动完成后，生成最终统计信息
    print(f"[截图] 所有滚动完成，共有 {len(merged_df)} 条对话")

    # 第四步：拼接图像生成最终长截图
    print("[截图] 生成最终聊天长截图")
    result_path = None  # 初始化结果路径为None

    if not merged_df.empty:
        # 从DataFrame中提取所有PIL图像对象到列表
        images_to_merge = merged_df['pil_image'].tolist()

        # 调用merge_images函数将所有图像垂直拼接为一张长图
        result_path = merge_images(images_to_merge)

        # 打印保存路径
        # print(f"聊天长截图已保存: {result_path}")
    else:
        # 如果没有有效内容，打印错误信息
        print("[错误] 没有找到有效对话内容，无法生成长截图")

    # 返回最终生成的长截图文件路径，如果失败则为None
    return result_path

# 获取小红书主页的名称和ID
def get_xiaohongshu_profile_info():
    """
    进入小红书用户主页并获取用户名和小红书号

    步骤:
        2. 获取用户名
        3. 获取小红书号

    返回:
        tuple: (nametext, xhs_number) - 用户名和小红书号
    """
    nametext = None
    xhs_number = None
    
    try:
        # 查找小红书名TextView控件
        TTextView_nodes = Selector().type("TextView").depth(16).clickable(True).find()
        # 提取控件文本 - 添加空值检查
        if TTextView_nodes:
            nametext = TTextView_nodes.text
        else:
            print("未找到用户名TextView控件")

        # 查找小红书号TextView控件
        ID_TextView_nodes = Selector().type("TextView").depth(18).clickable(True).find()
        # 提取控件文本 - 添加空值检查
        if ID_TextView_nodes:
            text = ID_TextView_nodes.text
            # 正则匹配提取号码
            number_match = re.search(r'小红书号：([^\n]+)', text)  # 匹配"小红书号："后面的所有非换行字符
            if number_match:
                xhs_number = number_match.group(1)
            else:
                print("小红书号格式不匹配")
        else:
            print("未找到小红书号TextView控件")

        # 查找【更多】TextView控件
        setup_nodes=Selector(6).desc("^更多$").find()
        if setup_nodes:
            # 点击
            BlueHid.click(setup_nodes.center_x, setup_nodes.center_y)
            time.sleep(1)

            # 采用文字识别查找复制链接按钮
            res = Ocr.paddleocr_v2(pattern = '复制')
            if res:
                # print(f"找到复制链接按钮: {res}")

                #点击复制链接
                BlueHid.click(res[-1].center_x,res[-1].center_y)
                time.sleep(1)

                # 从剪贴板中读取信息
                msg = Clipboard.get()
                
                # 提取用户ID从URL中
                url_id_match = re.search(r'/user/profile/([^?]+)', msg)
                if url_id_match:
                    user_id = url_id_match.group(1)
                    # print(f"从URL中提取到用户ID: {user_id}")

                else:
                    print("未能从URL中提取用户ID")
                
            else:
                print("查找复制链接按钮失败")

            
        else:
            print("在用户页未找到【更多】TextView控件")


    except Exception as e:
        print(f"获取小红书个人信息失败: {str(e)}")
    
    # 返回用户名和小红书号
    return nametext, xhs_number,user_id



#处理小红书聊天记录并调用扣子客服工作流
def process_xiaohongshu_chat():
    """
    处理小红书聊天记录并调用扣子客服工作流

    参数:
        workflow_id (str): 扣子工作流ID
        app_token (str): 飞书应用Token
        ACCESS_TOKEN (str): 扣子API访问令牌

    返回:
        dict/None: 扣子客服工作流处理结果，失败时返回None
    """

    # 初始化变量
    screenshot_path = None
    username = None
    xhs_id = None
    user_id = None

    try:
        # 参数验证
        if not all([config_module.expert_workflow_id, config_module.expert_app_token, config_module.coze_token]):
            print("[错误] 参数不完整")
            return None

        # 等待页面加载(必须)
        time.sleep(1)

        # 查找聊天对话页面，对方的头像 View控件
        View_nodes = Selector(6).type("^View$").clickable(True).find()

        if View_nodes:
            #打印点击位置
            print(f" 点击头像位置: {View_nodes.center_x}, {View_nodes.center_y}")
            # 点击头像，最多尝试3次
            max_attempts = 3
            attempt_count = 0
            success = False
            
            while attempt_count < max_attempts and not success:
                attempt_count += 1
                print(f"[尝试] 点击头像 ({attempt_count}/{max_attempts})")
                # 点击
                BlueHid.click(View_nodes.center_x, View_nodes.center_y)
                # 等待一小段时间让界面响应
                time.sleep(0.8)
                
                # 检查是否点击成功
                if Selector(6).desc("^更多$").find():
                    success = True
                    print(f"[成功] 头像点击成功，进入用户资料页")
                    # 等待页面加载(必须)
                    time.sleep(1.5)
                    # 获取小红书用户信息
                    username, xhs_id, user_id = get_xiaohongshu_profile_info()
                    break
                else:
                    print(f"[错误] 点击头像失败 (尝试 {attempt_count}/{max_attempts})")
                    time.sleep(0.5)  # 失败后短暂等待再试
            
            if not success:
                print("[错误] 多次尝试点击头像均失败，放弃处理")

                # 发消息给飞书日志表格
                add_log_data(
                    coze_token=config_module.coze_token, 
                    adddata_workflow_id=config_module.adddata_workflow_id, 
                    log_app_token=config_module.log_app_token, 
                    title="私信内容获取错误，多次尝试点击头像均失败，放弃处理", 
                    content=f"**设备编号**: {config_module.number}\n**我的账号**: {config_module.my_name}\n**我的ID**: {config_module.my_id}\n**目标账号**: {username or '未获取'}\n**目标ID**: {xhs_id or '未获取'}\n**错误原因**: 未打开小红书个人主页页面",
                    tag="私信内容采集"
                )
                
                BlueHid.back()  # 返回上一级
                return None
            

            if not username or not xhs_id or not user_id:
                print("[错误] 获取用户信息失败")

                # 发消息给飞书日志表格
                add_log_data(
                    coze_token=config_module.coze_token, 
                    adddata_workflow_id=config_module.adddata_workflow_id, 
                    log_app_token=config_module.log_app_token, 
                    title="私信内容获取错误，获取用户信息失败", 
                    content=f"**设备编号**: {config_module.number}\n**我的账号**: {config_module.my_name}\n**我的ID**: {config_module.my_id}\n**目标账号**: {username or '未获取'}\n**目标ID**: {xhs_id or '未获取'}\n**错误原因**: 未获取到用户信息",
                    tag="私信内容采集"
                )

                BlueHid.back()  # 返回上一级
                return None
            
            else:   
                print(f"[聊天] 用户: {username}, ID: {xhs_id}, 用户UID: {user_id}")

                # 返回聊天对话
                BlueHid.back()
                time.sleep(0.5)  # 给界面一点反应时间
                
                # 尝试最多4次返回操作
                max_return_attempts = 4
                for attempt in range(max_return_attempts):
                    # 检查是否已返回聊天界面(通过EditText控件判断)  
                    if Selector(6).type("EditText").find():
                        print(f"[聊天] 成功返回聊天界面 (尝试 {attempt+1}/{max_return_attempts})")
                        break
                    else:
                        print(f"[聊天] 未成功返回聊天界面 (尝试 {attempt+1}/{max_return_attempts})")
                        BlueHid.back()  # 再次尝试返回
                        time.sleep(1)  # 等待界面响应
                else:
                    # 循环正常结束但未找到EditText控件，说明所有尝试都失败
                    print("[错误] 无法返回聊天界面，放弃处理")

                    # 发消息给飞书日志表格
                    add_log_data(
                        coze_token=config_module.coze_token, 
                        adddata_workflow_id=config_module.adddata_workflow_id, 
                        log_app_token=config_module.log_app_token, 
                        title="私信内容获取错误，无法返回聊天界面，放弃处理", 
                        content=f"**设备编号**: {config_module.number}\n**我的账号**: {config_module.my_name}\n**我的ID**: {config_module.my_id}\n**目标账号**: {username or '未获取'}\n**目标ID**: {xhs_id or '未获取'}\n**错误原因**: 未返回聊天界面",
                        tag="私信内容采集"
                    )

                    return None  # 终止处理流程 
                

        else:
            print("[错误] 未找到对方的头像 View控件")   

            # 发消息给飞书日志表格
            add_log_data(
                coze_token=config_module.coze_token, 
                adddata_workflow_id=config_module.adddata_workflow_id, 
                log_app_token=config_module.log_app_token, 
                title="私信内容获取错误，未找到对方的头像 View控件", 
                content=f"**设备编号**: {config_module.number}\n**我的账号**: {config_module.my_name}\n**我的ID**: {config_module.my_id}\n**目标账号**: {username or '未获取'}\n**目标ID**: {xhs_id or '未获取'}\n**错误原因**: 未找到对方的头像 View控件",
                tag="私信内容采集"
            )

            BlueHid.back()  # 返回上一级
            return None


        # 快速滑动到聊天对话顶部
        loop_times = 3  # 指定循环次数
        for i in range(loop_times):
            BlueHid.swipM(int(wide / 2), int(High / 2 - 600), int(wide / 2), int(High / 2))
            time.sleep(2)  # 等待页面稳定

        # 调用函数滚动查询聊天记录
        screenshot_path = capture_chat_long_screenshot()

        # 验证截图生成成功
        if not screenshot_path:
            print("[错误] 截图生成失败")

            # 发消息给飞书日志表格
            add_log_data(
                coze_token=config_module.coze_token, 
                adddata_workflow_id=config_module.adddata_workflow_id, 
                log_app_token=config_module.log_app_token, 
                title="私信内容获取错误，截图生成失败", 
                content=f"**设备编号**: {config_module.number}\n**我的账号**: {config_module.my_name}\n**我的ID**: {config_module.my_id}\n**目标账号**: {username or '未获取'}\n**目标ID**: {xhs_id or '未获取'}\n**错误原因**: 未生成截图",
                tag="私信内容采集"
            )

            BlueHid.back()  # 返回上一级
            return None
        
        else:

            print("[聊天] 成功生成截图：", screenshot_path)

            #退出聊天页面（返回上一级  ，避免出现新聊天内容）
            BlueHid.back()

        # 上传到扣子文件存储空间获取ID
        ID_data = upload_image(config_module.coze_token, screenshot_path)
        if not ID_data:
            print("[错误] 文件上传失败，未获取到有效的文件ID")

            # 发消息给飞书日志表格
            add_log_data(
                coze_token=config_module.coze_token, 
                adddata_workflow_id=config_module.adddata_workflow_id, 
                log_app_token=config_module.log_app_token, 
                title="私信内容获取错误，文件上传失败，未获取到有效的文件ID", 
                content=f"**设备编号**: {config_module.number}\n**我的账号**: {config_module.my_name}\n**我的ID**: {config_module.my_id}\n**目标账号**: {username or '未获取'}\n**目标ID**: {xhs_id or '未获取'}\n**错误原因**: 未获取到有效的文件ID",
                tag="私信内容采集"
            )

            return None

        # 调用扣子客服工作流进行处理
        Chat_screenshot = [ID_data]  # 传入图片的file_id列表
        result = call_workflow(
            config_module.coze_token, 
            config_module.expert_workflow_id, 
            config_module.expert_app_token, 
            config_module.my_id, 
            config_module.my_name, 
            username, 
            xhs_id,
            user_id,
            Chat_screenshot
        )

        print(f"客服异步调用返回：{result}")

        if not result or result.get('code') != 0:
            error_msg = "返回空结果" if not result else f"返回错误码: {result.get('code')}, 信息: {result.get('msg', '无错误信息')}"
            print(f"[错误] 调用客服失败，{error_msg}")

            # 发消息给飞书日志表格
            add_log_data(
                coze_token=config_module.coze_token, 
                adddata_workflow_id=config_module.adddata_workflow_id, 
                log_app_token=config_module.log_app_token, 
                title="私信内容获取错误，调用客服失败", 
                content=f"**设备编号**: {config_module.number}\n**我的账号**: {config_module.my_name}\n**我的ID**: {config_module.my_id}\n**目标账号**: {username or '未获取'}\n**目标ID**: {xhs_id or '未获取'}\n**错误原因**: 调用客服失败，{error_msg}",
                tag="私信内容采集"
            )

            return None

        # 成功完成全部流程，不退出当前页面
        print("[聊天] 处理完成")
        return result

    except Exception as e:
        # 处理所有未预期的异常
        print(f"[错误] 聊天处理异常: {str(e)}")
        
        # 发消息给飞书日志表格
        add_log_data(
            coze_token=config_module.coze_token, 
            adddata_workflow_id=config_module.adddata_workflow_id, 
            log_app_token=config_module.log_app_token, 
            title="私信内容获取错误，聊天处理异常", 
            content=f"**设备编号**: {config_module.number}\n**我的账号**: {config_module.my_name}\n**我的ID**: {config_module.my_id}\n**目标账号**: {username or '未获取'}\n**目标ID**: {xhs_id or '未获取'}\n**错误原因**: 聊天处理异常: {str(e)}",
            tag="私信内容采集"
        )
         
        return None
    
    finally:

        # 统一的文件清理逻辑，无论成功或失败都会执行
        try:
            if screenshot_path:
                os.remove(screenshot_path)
                print("[清理] 已删除临时截图文件")
        except OSError as e:
            print(f"[警告] 文件删除失败: {str(e)}")
            # 文件清理失败不中断主流程

#==============================================
# 主程序
#==============================================

if __name__ == "__main__":

    # 调用主程序
    process_xiaohongshu_chat()




